#include <iostream>
#include <list>
#include <string>
using namespace std;

class PaymentStrategy 
{
public:
	virtual ~PaymentStrategy() {}
	virtual void payoff()=0;
};

class DefaultStrategy: public PaymentStrategy 
{
public:
	void payoff() {
		cout << "the payoff formula: amount = price * quantity" << endl;
	}
};

class Decorator: public PaymentStrategy 
{
private:
	PaymentStrategy* strategy;
public:
	void setStrategy(PaymentStrategy* strategy) 
	{
		this->strategy = strategy;
	}
	PaymentStrategy* getStrategy() 
	{
		return this->strategy;
	}
	Decorator(PaymentStrategy* strategy) 
	{
		this->setStrategy(strategy);
	}
	virtual void payoff()=0;
};

class DiscountStrategy: public Decorator 
{
public:
	DiscountStrategy(PaymentStrategy* strategy):Decorator(strategy){}
	void payoff() 
	{
		this->getStrategy()->payoff();
		cout << "the payoff formula: amount = amount * discount" << endl;
	}
};

class VipStrategy: public Decorator 
{
public:
	VipStrategy(PaymentStrategy* strategy):Decorator(strategy){}
	void payoff() 
	{
		this->getStrategy()->payoff();
		cout << "the payoff formula: amount = amount * vip_discount" << endl;
	}
};

class PaymentFactory 
{
private:
	PaymentFactory();
public:
	static PaymentFactory* getInstance() 
	{
		static PaymentFactory* instance;
		return instance;
	}
	PaymentStrategy* create(string type) 
	{
		if(type=="normal") return new DefaultStrategy();
		else if(type=="discount") return new DiscountStrategy(new DefaultStrategy());
		else if(type=="vip") return new VipStrategy(new DefaultStrategy());
		else if(type=="mix") return new VipStrategy(new DiscountStrategy(new DefaultStrategy()));
		return NULL;
	}
};

class Client 
{
private:
	PaymentStrategy* strategy;
public:
	void setStrategy(PaymentStrategy* strategy) 
	{
		this->strategy = strategy;
	}
	PaymentStrategy* getStrategy() 
	{
		return this->strategy;
	}
	Client(PaymentStrategy* strategy) 
	{
		setStrategy(strategy);
	}
	void payoff() 
	{
		this->strategy->payoff();
	}
};

int main() {
	PaymentFactory* factory = PaymentFactory::getInstance();
	cout << ">>> the normal payoff:" << endl;
	Client* client = new Client(factory->create("normal"));
	client->payoff();

	cout << ">>> the discount payoff:" << endl;
	client->setStrategy(factory->create("discount"));
	client->payoff();

	cout << ">>> the vip discount payoff:" << endl;
	client->setStrategy(factory->create("vip"));
	client->payoff();

	cout << ">>> the mix discount payoff:" << endl;
	client->setStrategy(factory->create("mix"));
	client->payoff();
	return 0;
}
